package com.cl.code.web.component.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import java.time.Duration;
import java.util.Collections;
import java.util.List;

/**
 * @author chengliang
 * @since 1.0.0
 */
@Order
public class WebLogFilter implements Filter {

    Logger log = LoggerFactory.getLogger("WEB_LOG");

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
        try {
            if (servletRequest instanceof ContentCachingRequestWrapper && servletResponse instanceof ContentCachingResponseWrapper) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }
            ContentCachingRequestWrapper request;
            request = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
            ContentCachingResponseWrapper response = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);

            //给返回设置traceId
            response.setHeader("Trace-Id", MDC.get("traceId"));

            // 继续请求 & 计算请求耗时
            long startTime = System.nanoTime();
            filterChain.doFilter(request, response);
            String path = request.getRequestURI();
            String method = request.getMethod();
            Duration requestDuration = Duration.ofNanos(System.nanoTime() - startTime);
            long cost = requestDuration.toMillis();
            String userAgent = request.getHeader("User-Agent");
            String contentType = request.getHeader("Content-Type");
            String authorization = request.getHeader("Authorization");

            // 解析ip
            String ip = deduceIP(request);

            // parameter
            List<String> parameterNames = Collections.list(request.getParameterNames());
            String parameterString = "";
            if (!parameterNames.isEmpty()) {
                StringBuilder paramBuilder = new StringBuilder();
                for (String parameterName : parameterNames) {
                    String parameterValue = request.getParameter(parameterName);
                    paramBuilder.append(parameterName).append("=").append(parameterValue).append("&");
                }
                parameterString = paramBuilder.substring(0, paramBuilder.length() - 1);
            }

            // 请求体
            String requestBody = "";
            requestBody = new String(request.getContentAsByteArray(), request.getCharacterEncoding());
            String responseBody = new String(response.getContentAsByteArray(), response.getCharacterEncoding());
            String stringBuilder = "\npath:" + path +
                    "\nmethod:" + method +
                    "\nip:" + ip +
                    "\nuserAgent:" + userAgent +
                    "\ncontentType:" + contentType +
                    "\nauthorization:" + authorization +
                    "\ncost:" + cost + " ms" +
                    "\nparameter:" + parameterString +
                    "\nrequestBody:" + requestBody +
                    "\nresponseBody:" + responseBody +
                    "\nstatus:" + response.getStatus();
            // 打印日志
            log.info(stringBuilder);
            response.copyBodyToResponse();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String deduceIP(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (checkIP(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (checkIP(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (checkIP(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    private static boolean checkIP(String ip) {
        return ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)
                || ip.split("\\.").length != 4;
    }

}
